import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

public class TemplateProjectCreator extends ProjectCreator {

    public TemplateProjectCreator(String srcRootDir, MakeParam makeParam, String projectRootDir) {
        super(srcRootDir, makeParam, projectRootDir);
    }
    
    @Override
    public void create() throws Exception {
        
        /* copy template project */
        FileUtils.copyDir(new File("./res/template"), new File(getProjectRootDir()));
        
        /* use makeParam to replace project */
        modifyCProject();
        modifyProject();
    }
    
    private void modifyCProject() {
        
        File cProjectFile = new File(getProjectRootDir(), ".cproject");
        String cProjectText = FileUtils.readFile(cProjectFile);
        /* set build variable (root directory) */
        cProjectText = cProjectText.replace("[SRC_ROOT_DIR]", getSrcRootDir());
        /*
         * add several content 
         */
        cProjectText = setCompiler(cProjectText);
        cProjectText = addIncludeFiles(cProjectText);
        cProjectText = addIncludePaths(cProjectText);
        cProjectText = addDefines(cProjectText);
        FileUtils.writeFile(cProjectFile, cProjectText);
    }
    
    private void modifyProject() {
        
        File projectFile = new File(getProjectRootDir(), ".project");
        String projectText = FileUtils.readFile(projectFile);
        /* set variable (root directory) */
        projectText = projectText.replace("[SRC_ROOT_DIR]", getSrcRootDir());
        /*
         * add several content
         */
        projectText = addSrcAndHeader(projectText);
        FileUtils.writeFile(projectFile, projectText);
    }
    
    private String setCompiler(String projectText) {
        String compiler = getMakeParam().getCompiler();
        if(compiler.endsWith("-gcc")) {
            compiler = compiler.substring(0, compiler.length() - "gcc".length());
        }
        
        projectText = projectText.replace("[COMPILER_PREFIX]", compiler);
        return projectText;
    }
    
    private String addCProjectOptions(String projectText, String tagName, 
            Map<String, StringItem> paramMap, String rootDir) {
        StringBuffer options = new StringBuffer();
        for(Entry<String, StringItem> entry : paramMap.entrySet()) {
            String value = entry.getValue().getValue();
            /* add root dir */
            if(rootDir != null) {
//                /* remove ./ */
//                if(value.startsWith("./")) {
//                    value = value.substring("./".length());
//                }
            	value = correctFilePath(value);
                value = rootDir + value;
            }
            /* replay " with &quot; */
            value = value.replace("\"", "&quot;");
            /* create option */
            String option = String.format(Locale.getDefault(), 
                    "<listOptionValue builtIn=\"false\" value=\"&quot;%s&quot;\"/>%s", 
                    value, System.lineSeparator());
            options.append(option);
        }
        projectText = projectText.replace(tagName, options);
        return projectText;
    }
    
    private String addIncludePaths(String projectText) {
        projectText = addCProjectOptions(projectText, "[INCLUDE_PATHS]", getMakeParam().getIncludeDirs(), "${SRC_ROOT_DIR}/");
        return projectText;
    }
    
    private String addIncludeFiles(String projectText) {
        projectText = addCProjectOptions(projectText, "[INCLUDE_FILES]", getMakeParam().getIncludeFiles(), "${SRC_ROOT_DIR}/");
        return projectText;
    }
    
    private String addDefines(String projectText) {
        projectText = addCProjectOptions(projectText, "[DEFINES]", getMakeParam().getDefines(), null);
        return projectText;
    }
    
    private String addSrcAndHeader(String projectText) {
        String srcFiles = makeSrcFiles();
        String headerFiles = makeHeaderFiles();
        StringBuffer files = new StringBuffer();
        files.append("<linkedResources>" + System.lineSeparator());
        files.append(srcFiles + System.lineSeparator());
        files.append(headerFiles + System.lineSeparator());
        files.append("</linkedResources>" + System.lineSeparator());
        
        projectText = projectText.replace("<linkedResources/>", files);
        return projectText;
    }
    
    private String makeSrcFiles() {
        
        return createFileCfg(getMakeParam().getSrcFiles());
    }
    
    private String makeHeaderFiles() {
        List<String> headerList = new ArrayList<String>();
        for(Entry<String, StringItem> entry : getMakeParam().getDependHeaderFiles().entrySet()) {
            headerList.add(entry.getValue().getValue());
        }
        return createFileCfg(headerList);
    }
    
    private String createFileCfg(List<String> files) {
        
        StringBuffer buffer = new StringBuffer();
        HashSet<String> virtualDirs = new HashSet<String>();
        
        for(String file : files) {
//            if(!file.startsWith(srcDirPath)) {
//                System.err.println("error src:" + file);
//                continue;
//            }
            
            String correctFilePath = correctFilePath(file);
            if(correctFilePath == null) {
                System.err.println("error file path:" + file);
                continue;
            }
            
            List<String> dirList = enumDirs(correctFilePath);
            for(String dir : dirList) {
                if(virtualDirs.contains(dir)) {
                    continue;
                }
                virtualDirs.add(dir);
                buffer.append(createLink(dir, "2", "virtual:/virtual"));
            }
            
            String srcLocationURI = "SRC_ROOT_DIR" + File.separator + correctFilePath;
            buffer.append(createLink(correctFilePath, "1", srcLocationURI));
        }
        return buffer.toString();
    }
    
    private List<String> enumDirs(String path) {
        List<String> dirList = new ArrayList<String>();
        int index = 0;
        while(true) {
            index = path.indexOf(File.separator, index);
            if(index == -1) {
                break;
            }
            
            dirList.add(path.substring(0, index));
            index += File.separator.length();
        }
        
        return dirList;
    }
    
    private String createLink(String name, String type, String locURI) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("\t\t<link>" + System.lineSeparator());
        buffer.append("\t\t\t<name>" + name + "</name>" + System.lineSeparator());
        buffer.append("\t\t\t<type>" + type + "</type>" + System.lineSeparator());
        buffer.append("\t\t\t<locationURI>" + locURI + "</locationURI>" + System.lineSeparator());
        buffer.append("\t\t</link>" + System.lineSeparator());
        
        return buffer.toString();
    }
    
    private String correctFilePath(String path) {
        if(path.startsWith("/")) {
            if(path.startsWith(getSrcRootDir())) {
                return path.substring(getSrcRootDir().length());
            } else {
                return null;
            }
        } else if(path.startsWith("./")) {
            return path.substring("./".length());
        } else {
            return path;
        }
        
    }
        
}
